package at.jaki.ubb.crawling.core;

import org.apache.log4j.Logger;

import at.jaki.ubb.crawling.api.CrawlingEngine;
import at.jaki.ubb.crawling.appThreads.DataFinderResultManager;
import at.jaki.ubb.crawling.appThreads.DataSet;
import at.jaki.ubb.crawling.appThreads.DataTransformerResultManager;
import at.jaki.ubb.crawling.appThreads.DbWrapper;
import at.jaki.ubb.crawling.appThreads.TargetQueue;
import at.jaki.ubb.crawling.components.DataInterpreterWrapper;
import at.jaki.ubb.crawling.dataFinder.DataFinderManager;
import at.jaki.ubb.crawling.dataTransformer.DataTransformerManager;
import at.jaki.ubb.crawling.exceptions.InitializationFailedException;
import at.jaki.ubb.crawling.model.EngineStatus;
import at.jaki.ubb.crawling.model.Target;
import at.jaki.ubb.crawling.model.config.CrawlingEngineConfig;
import at.jaki.ubb.crawling.model.config.DataFinderManagerConfig;
import at.jaki.ubb.crawling.model.config.DataTransformerManagerConfig;
import at.jaki.ubb.crawling.model.config.TargetQueueConfig;

public class CrawlingEngineImpl implements CrawlingEngine {

	private static final Logger LOG = Logger.getLogger(CrawlingEngineImpl.class);

	private CrawlingEngineConfig lastUsedConfig = null;

	private void startTargetQueue(TargetQueueConfig targetQueueConfig) {
		TargetQueue targetQueue = TargetQueue.getInstance();
		targetQueue.initialize(targetQueueConfig);
		ThreadPool.getInstance().submit(targetQueue);
	}

	private void startDataFinderResultManager() {
		DataFinderResultManager dataFinderResultManager = DataFinderResultManager.getInstance();
		ThreadPool.getInstance().submit(dataFinderResultManager);
	}

	private void startDataSet() {
		DataSet dataSet = DataSet.getInstance();
		ThreadPool.getInstance().submit(dataSet);
	}

	private void startDataTransformerResultManager() {
		DataTransformerResultManager dataTransformerResultManager = DataTransformerResultManager.getInstance();
		ThreadPool.getInstance().submit(dataTransformerResultManager);
	}

	private void startDbWrapper(String clazz) throws InitializationFailedException {
		if (clazz == null) {
			throw new InitializationFailedException("DbWrapper class must be specified");
		}
		DbWrapper.init(clazz);
		ThreadPool.getInstance().submit(DbWrapper.getInstance());
	}

	private void initDataInterpreter(String dataInterpreterClass) throws InitializationFailedException {
		if (dataInterpreterClass == null) {
			throw new InitializationFailedException("Data interpreter class must be specified");
		}
		DataInterpreterWrapper.init(dataInterpreterClass);
	}

	private void initDataFinders(DataFinderManagerConfig configObject) throws InitializationFailedException {
		boolean noErrors = DataFinderManager.init(configObject);
		if (!noErrors) {
			throw new InitializationFailedException("Could not initialize Data finders");
		}
	}

	private void initDataTransformers(DataTransformerManagerConfig configObject) throws InitializationFailedException {
		boolean noErrors = DataTransformerManager.init(configObject);
		if (!noErrors) {
			throw new InitializationFailedException("Could not initialize Data transformers");
		}
	}

	private CrawlingEngineConfig parseConfigXml(String xml) {
		// TODO implement this
		throw new IllegalStateException("Not implemented... yet");
	}

	private Target<?> parseTargetXml(String xml) {
		// TODO Auto-generated method stub
		throw new IllegalStateException("Not implemented... yet");
	}

	@Override
	public void start(String xml) {
		start(parseConfigXml(xml));
	}

	@Override
	public void start(CrawlingEngineConfig config) {
		startDataSet();

		startDataFinderResultManager();
		startDataTransformerResultManager();

		try {
			initDataInterpreter(config.getDataInterpreterClass());
			startDbWrapper(config.getDbFacadeClass());
			startTargetQueue(config.getTargetQueueConfig());
			initDataFinders(config.getDataFinderManagerConfig());
			initDataTransformers(config.getDataTransformerManagerConfig());

			lastUsedConfig = config;
		} catch (InitializationFailedException e) {
			LOG.error("Could not start engine " + e.getMessage());
			LOG.info("Terminating app");
			stop();
		} catch (RuntimeException e) {
			LOG.error("Could not start engine " + e.getClass().getName() + ':' + e.getMessage());
			LOG.info("Terminating app");
			stop();
		}

	}

	@Override
	public void stop() {
		DataFinderResultManager.getInstance().stop(true);
		DataTransformerResultManager.getInstance().stop(true);
		TargetQueue.getInstance().stop(true);
		DataSet.getInstance().stop(true);
		DbWrapper.getInstance().stop(false);
		ThreadPool.getInstance().shutdown();
	}

	@Override
	public void addTarget(String xml) {
		addTarget(parseTargetXml(xml));
	}

	@Override
	public void addTarget(Target<?> t) {
		TargetQueue.getInstance().push(t);
	}

	@Override
	public EngineStatus getStatus() {
		int targetCount = TargetQueue.getInstance().getSize();
		int dataCount = DataSet.getInstance().getSize();
		int dataFinderCount = DataFinderResultManager.getInstance().getRunningThreadsCount();
		int dataTransformerCount = DataTransformerResultManager.getInstance().getRunningThreadsCount();
		EngineStatus engineStatus = new EngineStatus();
		engineStatus.setDataSetSize(dataCount);
		engineStatus.setRunningDataFinders(dataFinderCount);
		engineStatus.setRunningDataTransofrmers(dataTransformerCount);
		engineStatus.setTargetQueuSize(targetCount);
		return engineStatus;
	}

	@Override
	public void startWithLastConfig() throws IllegalStateException {
		if (lastUsedConfig == null) {
			throw new IllegalStateException();
		}
		start(lastUsedConfig);
	}

	@Override
	public boolean isStopped() {
		return !TargetQueue.getInstance().isRunning() && !DataSet.getInstance().isRunning() && !DbWrapper.getInstance().isRunning()
				&& !DataFinderResultManager.getInstance().isRunning() && !DataTransformerResultManager.getInstance().isRunning();
	}

}
